// 侧边栏用户列表菜单项点击后显示的组件

import React,{ useState, useEffect, useRef } from 'react'
import axios from 'axios'
// 引入 antd 组件库组件
import { Table,Button,Modal,Switch } from 'antd'
// 引入 antd 组件图标
import { DeleteOutlined,EditOutlined } from '@ant-design/icons'
// 引入用于添加用户的表单组件
import UserForm from '../../../components/UserForm/UserForm'
const { confirm } = Modal

export default function UseList() {

  // 定义一个变量用于存储表格数据
  const [dataSource, setdataSource] = useState([])

  // 控制添加用户的弹出框组件Modal是否显示
  const [isAddVisible,setIsAddVisible] = useState(false)

  // 控制更新用户权限的弹出框组件Modal是否显示
  const [isUpdateVisible,setIsUpdateVisible] = useState(false)

  // 控制更新用户权限的弹出框组件Modal下的UserForm组件中的用于选择区域的下拉组件Select是否禁用
  const [isUpdateDisabled,setIsUpdateDisabled] = useState(false)

  // 存储当前点击的更新用户权限的Button组件所在的表格项元素
  const [currentUpdate,setCurrentUpdate] = useState(null)

  // 存储角色分类
  const [roleList, setRoleList] = useState([])

  // 存储区域分类
  const [regionList, setRegionList] = useState([])

  // useRef创建出来的addForm用于获取指定的组件实例
  const addForm = useRef(null)  // 用于获取控制添加用户的弹出框组件Modal中的UserForm组件下的Form表单组件实例
  const updateForm = useRef(null) // 用于获取控制更新用户权限的弹出框组件Modal中的UserForm组件下的Form表单组件实例

  
  // 从本地存储中获取token数据，并且解构赋值
  // roleId 是当前用户角色对应的id值，region 是当前用户所在的区域， username 是当前用户的名称
  const { roleId, region, username } = JSON.parse(localStorage.getItem("token"))
  
  // 下面这个对象中存储的是id值对应的角色名称
  const roleObj = {
    // 当roleId值为1的时候，角色名称为superadmin，也就是 超级管理员
    "1": "superadmin",
    // 当roleId值为2的时候，角色名称为admin，也就是 区域管理员
    "2": "admin",
    // 当roleId值为2的时候，角色名称为editor，也就是 区域编辑
    "3": "editor",
  }

  useEffect(() => {
      // 获取区域信息
      axios.get("/regions").then(res => {   // res 接收到的是请求成功后返回的数据
          // console.log(res.data);
          setRegionList(res.data)
      })
  }, [])

  useEffect(() => {
      // 获取角色信息
      axios.get("/roles").then(res => {   // res 接收到的是请求成功后返回的数据
          // console.log(res.data);
          setRoleList(res.data)
      })
  }, [])

  /* 
    useEffect钩子中的第一次参数是一个函数，它会在组件渲染完成后执行，第二个参数用于设置依赖项列表(当依赖项发生变化时，才会重新执行回调函数)
    这里的依赖项列表是一个空数组，所以这个useEffect只会在组件初始化时执行一次
  */
  useEffect(()=>{
    // 获取用户表格组件Table所需的数据
    // axios 请求成功后会返回一个成功的Promise，并且将请求成功返回的数据作为参数传递给then方法的第一个箭头函数，如果请求失败，就会将失败结果传递给then方法中的第二个箭头函数(这里没有将第二个箭头函数写出来)
    axios.get("/users?_expand=role").then(res=>{   // res 接收到的是请求成功后返回的数据
      const list = res.data
      setdataSource(roleObj[roleId] === "superadmin" ? res.data : [
        // 对能够展示在Table组件上的数据进行筛选
        // 展示 用户名称 与自己相同的数据，也就是当前用户本身的数据
        ...list.filter(item => item.username === username),
        // 展示 区域 和 角色名称 与当前用户相匹配的数据
        ...list.filter(item => item.region === region && roleObj[item.roleId] === "editor"),
      ])
    })
  },[])

  
  // 控制添加用户的弹出框组件Modal的确认按钮的点击的事件回调
  const addFormOk = ()=>{
    // console.log(addForm);
    // addForm.current.validateFields() 方法用于读取数据
    // 如果读取成功，就会返回一个成功状态的Promise对象，从而就会执行后面的then方法
    // 如果读取失败就会它会返回一个失败状态的Promise对象，从而就会执行catch方法
    addForm.current.validateFields().then((value)=>{  // value接收的是读取成功的结果
      // console.log(value);
      // 隐藏弹出框组件
      setIsAddVisible(false)
      // 清空addForm实例下的表单组件中的所有内容
      addForm.current.resetFields()
      // 将 用户信息 发送到后端，让后端生成id值，在将这个用户信息设置给dataSource，这样方便后面的删除和更新功能
      axios.post('/users',{
        ...value,   // res接收的是UserForm组件中Form组件下的所有表单项的内容，这里将接收到的内容全部展开放到一个对象中
        "roleState": true,
        "dafault": false
      }).then((res)=>{  // res 是请求成功后后端返回的用户相关的数据
        setdataSource([...dataSource,{
          ...res.data,
          role:roleList.filter((item)=>{
            return item.id===value.roleId
          })[0]
        }]) // 将dataSource展开，并且将 dataSource 中与 后面那个对象 对应的属性更新成 res.data
      })
    }).catch((err)=>{   // err 接收的是读取失败的结果
      console.log(err);
    })
  }

  // 控制修改用户信息的弹出框组件Modal的确认按钮的点击的事件回调
  const updateFormOk = ()=>{
    // update.current获取的是UserForm组件下的Form组件实例
    // validateFields()方法可以获取到这个Form表单组件下的所有表单项的内容，如果获取成功，那么后面的then方法中的value值接收的就是这个内容
    updateForm.current.validateFields().then((value)=>{
      // console.log(value);

      setIsUpdateVisible(false)

      setdataSource(dataSource.map((item)=>{
        if(item.id === currentUpdate.id){ // currentUpdate存储的是当前点击的更新用户权限的Button组件所在的表格项元素
          return{
            ...item,
            ...value,
            role:roleList.filter((data) => {
              return data.id === value.roleId
            })[0]
          }
        }
        // 这里的item是dataSource中每次遍历出来的数据
        return item // 这里返回的item会去替换掉dataSource中item对应的数据
      }))
      setIsUpdateDisabled(!isUpdateDisabled) 

      axios.patch(`/users/${currentUpdate.id}`,value)
    })
  }

  // 删除用户按钮Button组件的点击事件回调
  const deleteMethod = (item)=>{
    console.log(item);  // item 接收的是当前点击的删除按钮所在的表格项元素
    setdataSource(dataSource.filter((data)=>{
      // 在 dataSource 中根据id 找到该表格项元素，并且将其筛选掉
      return data.id !== item.id
    }))

    // axios.delete(`/users/${item.id}`)
  }

  // 更新用户信息按钮Button组件的点击事件回调
  const handleUpdate = (item)=>{
    // 将当前点击的Button组件所在的表格项元素存到currentUpdate中
    setCurrentUpdate(item)

    setIsUpdateVisible(true)  // react状态更新有延迟
    /* 
      这段代码中的 setTimeout 函数并没有实际的延迟值，延迟时间设置为0。它的作用并不是为了延迟执行代码，而是为了将代码放入下一个事件循环中执行。

      具体原因是这段代码中涉及了React的状态更新延迟问题。在React中，状态更新是异步进行的，不会立即生效。通过将指定代码放入 setTimeout 中，可以确保指定代码在下一个事件循环中执行，从而等待React状态更新完成。

      将 updateForm.current.setFieldsValue(item) 放入定时器中是为了确保 上面的代码 setIsUpdateVisible(true) 执行完毕，react组件状态更新完毕后再执行

      因为定时器中的代码涉及了组件实例，而这个组件必须在上面代码执行完毕，react状态更新完毕后才能获取到，否则会报错
    */
    setTimeout(()=>{

      if(item.roleId===1){  // roleId等于1时，说明该用户的角色名称是 超级管理员
        // 那么就要禁用UserForm组件中Form组件下的用于选择区域的下拉组件Select
        setIsUpdateDisabled(true)
      }else{
        // 取消禁用
        setIsUpdateDisabled(false)
      }

      /* 
        item是当前点击的Button组件所在的表格项元素
        通过 updateForm 获取到UserForm组件下的Form组件实例，利用setFieldsValue方法修改Form组件下的表单项的内容
        setFieldsValue方法中需要一个对象，这个对象中的属性的属性名得是要被修改的表格项的name值，属性值是要被修改成的内容。
        而这种格式正好和item匹配，item是一个对象，这个对象中的元素的属性名正好是要被修改的表格项的name值，属性值是要被修改成的内容。
        所以可以直接把item放在setFieldsValue方法中
        具体的可以去antd的4版本查看Form表单组件的文档
       */
      updateForm.current.setFieldsValue(item)
    },0)
  }

  // 开关按钮组件Switc状态转换后触发的事件回调
  const handleChange = (item)=>{
    console.log(item);  // item 接收的是当前点击的删除按钮所在的表格项元素
    item.roleState = !item.roleState  // Switch组件的开关状态由roleState决定，这里是当Switch组件的状态改变的时候，相应的将roleState取反，
    // 因为表格是受dataSource控制的受控组件，当dataSource变化的时候，表格组件Table才会更新
    setdataSource([...dataSource])
    // item 是dataSource中的元素，item更新后，dataSource也会更新，不过item是dataSource中深层次的数据
    // 下面那种更新dataSource的方法是浅复制，它并不会检测到深层次数据的变化，所以要用上面那种方式，将dataSource展开，这样才会检测到深层次数据的变化
    // setdataSource(dataSource)  // 这种方式无效

    // 与后端同步更新数据
    axios.patch(`/users/${item.id}`,{
      roleState:item.roleState
    })
  }

  // antd 提供的写法，有规律，这里如果有不明白就查看 antd 4版本的文档
  const columns = [
    {
      title: '区域',
      dataIndex: 'region',
      // filters 的作用是让当前这一列表格能够进行筛选功能，filters 的值是一个数组，数组中状态的都是筛选字段
      filters:[
        // regionList存储的是区域字段
        ...regionList.map(item => {
          return ({  // regionList经过map方法遍历后，影响的是原数组，再用...扩展运算符将这个数组的元素展开
            text: item.label,
            value: item.value
          })
        }),
        {
          text:"全球",
          value:"全球"
        }
      ],
      // onFilter 配合上面的 filters，做出实际的筛选操作
      onFilter:(value,item)=>{
        if(value === "全球"){
          return item.region === ""
        }
        return item.region === value
      },
      render:(region)=>{  // region获取到的是当前这一列下表格项的内容
        return <b>{region===""?"全球":region}</b>
      }
    },
    {
      title: '角色名称',
      dataIndex: 'role',
      // render是给当前这一列中的所有表格都渲染对应的内容
      render: (role) => {
        return role.roleName
      }
    },
    {
      title: '用户名',
      dataIndex: 'username',
    },
    {
      title: '用户状态',
      dataIndex: 'roleState',
      render: (roleState, item) => {
        return <Switch 
          checkedChildren="开启" 
          unCheckedChildren="关闭" 
          checked={roleState} 
          disabled={item.default} 
          onChange={() => { 
            handleChange(item)
          }} 
        />
      }
    },
    {
      title: '操作',
      render: (item)=>{   // item 装的是当前这一行对应的元素
        return <div>
          <Button 
            type="danger" 
            shape="circle" 
            icon={<DeleteOutlined/>}
            onClick={()=>{
              deleteMethod(item)
            }}
            style={{marginRight:"5px"}}
            disabled={item.default} 
           />

            <Button 
              type="primary" 
              shape="circle" 
              icon={<EditOutlined/>} 
              disabled={item.default} 
              onClick={()=>{
                handleUpdate(item)
              }}
            />
        </div>
      }
    }
  ];
  
  return (
    <div>
      <Button style={{ marginBottom: "16px" }} type="primary" onClick={()=>{ setIsAddVisible(true) }}>添加用户</Button>

      {/* 
          columns 表格列的配置描述(每一列的开头的设置)
          dataSource 数据数组，设置每一行具体的信息
          pagination 给表格添加分页器
              pageSize 设置分页器每一页能够显示的信息条数
          rowKey 指定属性作为当前表格项的key，下面是将表格项元素的id值作为表格项的key
      */}
      <Table 
        dataSource={dataSource} 
        columns={columns} 
        pagination={{
          pageSize: 5
        }}
        // item 是当前表格项元素
        rowKey={(item)=>{return item.id}}
      />

      {/* 用于添加用户的弹出框控件 */}
      {/* 
        title 用于设置组件标题
        open 用于设置当前组件的显示与隐藏，当值为true的时候显示，为false的时候隐藏
        onOk 是当组件的确认按钮被点击后触发的事件
        onCancel 是当组件的取消按钮被点击后触发的事件
      */}
      <Modal
        open={isAddVisible}
        title="添加用户"
        okText="确定"
        cancelText="取消"
        onCancel={()=>{ setIsAddVisible(false) }}
        onOk={() => {
          addFormOk()
        }}
      >
        {/* 将 roleList 和 regionList 传递给子组件 UserForm */}
        {/* 下面还将 addForm 作为 ref 属性值传递给 UserForm 子组件，子组件中只要把ref属性绑定给对应的组件，这里就可以通过 addForm 获取到对应的组件实例 */}
        {/* 子组件中是将 ref 属性绑定给了 Form 组件，所以这里可以通过 addForm 获取到 Form 组件的实例 */}
        <UserForm roleList={roleList} regionList={regionList} ref={addForm} />
      </Modal>

      {/* 用于修改用户信息弹出框控件 */}
      <Modal
        open={isUpdateVisible}
        title="修改用户权限"
        okText="修改"
        cancelText="取消"
        onCancel={()=>{ 
          setIsUpdateVisible(false); 
          setIsUpdateDisabled(!isUpdateDisabled) 
        }}
        onOk={() => {
          updateFormOk()
        }}
      >
        <UserForm 
          roleList={roleList} 
          regionList={regionList} 
          ref={updateForm} 
          isUpdateDisabled={isUpdateDisabled}
          // 给UserForm组件传递isUpdate属性，让UserForm组件能够判断当前处于修改用户信息操作
          isUpdate={true}
        ></UserForm>
      </Modal>
    </div>
  )
}
